import { BaseSystem } from '../base';
import { FEATURE_UNLOCK_ID } from '../../data/feature-unlock/type';
import { EVENTS, type IEventBus } from '../../events';
import type { CultivationModel } from '../../model/cultivation';
import type { RealmModel } from '../../model/realm';
import type {
  CultivationSkillModel,
  OfflineEarningsModel,
  PlayerProgressModel,
  SectModel,
} from '../../model';
import type { PlayerStateSystem } from '../player-state';
import { MODEL_NAME } from '../../constants/model-names';
import type { OfflineEarningsRealmInfo } from '../../model/offline-earnings/type';

export class CultivationSystem extends BaseSystem {
  private bus: IEventBus;
  private playerStateSystem: PlayerStateSystem;

  constructor(bus: IEventBus, playerStateSystem: PlayerStateSystem) {
    super();
    this.bus = bus;
    this.playerStateSystem = playerStateSystem;

    this.bus.on(EVENTS.CULTIVATION_START, () => {
      const cultivationModel =
        this.playerStateSystem.getModel<CultivationModel>(
          MODEL_NAME.CULTIVATION
        );
      cultivationModel.startCultivation();
    });

    this.bus.on(EVENTS.FEATURE_UNLOCK_SUCCESS, (id) => {
      if (id === FEATURE_UNLOCK_ID.AUTO_CULTIVATION) {
        const cultivationModel =
          this.playerStateSystem.getModel<CultivationModel>(
            MODEL_NAME.CULTIVATION
          );
        cultivationModel?.startAutoCultivation();
      }
    });

    this.bus.on(EVENTS.CULTIVATION_COMPLETED, () => {
      this.cultivationCompleted();
    });
  }

  /** 计算功法修为获取加成值 */
  private calcCultivationKillsGetExp() {
    const cultivationSkillModel =
      this.playerStateSystem.getModel<CultivationSkillModel>(
        MODEL_NAME.CULTIVATION_SKILL
      );
    if (!cultivationSkillModel) return 0;
    let exp = 0;
    const skillsInfo =
      cultivationSkillModel.getCultivationSkillUpgradeProgressInfo();
    skillsInfo.forEach((skill) => {
      exp +=
        skill.cumulativeSpeedBonus + skill.skillConfig.baseCultivationSpeed;
    });
    return exp;
  }

  /** 计算宗门修炼场加成 */
  private calcSectCultivationSpotMultiplier() {
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    if (!sectModel) return 0;
    const currentCultivationSpotConfig =
      sectModel.getCurrentCultivationSpotConfig();
    if (!currentCultivationSpotConfig) return 0;
    if (sectModel.hasSectCultivationSpotValidCount() === false) return 0;
    return currentCultivationSpotConfig.cultivationMultiplier - 1;
  }

  /** 设置修为离线收益 */
  private setRealmEarnings() {
    const offlineEarningsModel =
      this.playerStateSystem.getModel<OfflineEarningsModel>(
        MODEL_NAME.OFFLINE_EARNINGS
      );
    const cultivationModel = this.playerStateSystem.getModel<CultivationModel>(
      MODEL_NAME.CULTIVATION
    );
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    if (!offlineEarningsModel || !cultivationModel || !sectModel) return;
    const isAutoCultivating = cultivationModel.getIsAutoCultivating();
    if (!isAutoCultivating) return;
    const realmInfo: OfflineEarningsRealmInfo = {
      consumeSectCultivationSpotCount: 0,
      sectCultivationSpotValidCount: 0,
      consumeSectCultivationSpotCountExp: 0,
      cultivationKillsExp: 0,
    };
    const offlineDiffSeconds = offlineEarningsModel.getOfflineDiffTime() / 1000;
    /** 总共修炼次数 */
    const cultivationCount = Math.floor(offlineDiffSeconds / 5);
    /** 所有功法加成的修为 */
    const cultivationKillsExp = this.calcCultivationKillsGetExp();
    const sectCultivationSpotMultiplier =
      this.calcSectCultivationSpotMultiplier();
    /** 宗门修炼场剩余有效次数 */
    const sectCultivationSpotValidCount =
      sectModel.getSectCultivationSpotValidCount();
    if (sectCultivationSpotValidCount > cultivationCount) {
      realmInfo.consumeSectCultivationSpotCount = cultivationCount;
      realmInfo.sectCultivationSpotValidCount =
        sectCultivationSpotValidCount - cultivationCount;
      realmInfo.consumeSectCultivationSpotCountExp = Math.floor(
        cultivationCount * cultivationKillsExp * sectCultivationSpotMultiplier
      );
    } else {
      realmInfo.consumeSectCultivationSpotCount = Math.max(
        0,
        sectCultivationSpotValidCount
      );
      realmInfo.sectCultivationSpotValidCount = 0;
      realmInfo.consumeSectCultivationSpotCountExp = Math.max(
        0,
        Math.floor(
          sectCultivationSpotValidCount *
            cultivationKillsExp *
            sectCultivationSpotMultiplier
        )
      );
    }
    realmInfo.cultivationKillsExp = cultivationCount * cultivationKillsExp;

    const totalExp =
      realmInfo.consumeSectCultivationSpotCountExp +
      realmInfo.cultivationKillsExp;
    offlineEarningsModel.setRealmEarnings(totalExp);
    offlineEarningsModel.setRealmInfo(realmInfo);
  }

  /** 修炼完成 */
  private cultivationCompleted() {
    const realmModel = this.playerStateSystem.getModel<RealmModel>(
      MODEL_NAME.REALM
    );
    const playerProgressModel =
      this.playerStateSystem.getModel<PlayerProgressModel>(
        MODEL_NAME.PLAYER_PROGRESS
      );
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    if (!realmModel || !playerProgressModel || !sectModel) return;
    const cultivationKillsExp = this.calcCultivationKillsGetExp();
    const sectCultivationSpotMultiplier =
      this.calcSectCultivationSpotMultiplier();
    const totalMultiplier = 1 + sectCultivationSpotMultiplier;

    const totalExp = cultivationKillsExp * totalMultiplier;

    realmModel.addExp(totalExp);

    if (sectCultivationSpotMultiplier > 0) {
      /** 减少宗门修炼场可用次数 */
      sectModel.reduceSectCultivationSpotValidCount();
    }
    /** 初始化游戏创建时间 */
    playerProgressModel.setCreatedAt(Date.now());
  }

  public override initAfter(): void {
    this.setRealmEarnings();
  }

  /**
   * 更新函数
   * @param dt 秒
   */
  public override update(dt: number) {
    const cultivationModel = this.playerStateSystem.getModel<CultivationModel>(
      MODEL_NAME.CULTIVATION
    );
    cultivationModel.updateCultivationTime(dt);
  }
}
